﻿using System.Reflection;
using ApiConceptsFramework.FluentApi;
using NUnit.Framework;
using DIApi.Attributes;
using test.ApiConceptsFramework.FluentApi.Scenarios.TestClasses;

namespace test.ApiConceptsFramework.FluentApi.Scenarios
{
	[TestFixture, Explicit]
	public class DiApiAttributesTests
	{
		private Assembly[] _assemblies;
		private string[] _usings;
		private ApiEvaluator _eval;

		#region Setup
		[SetUp]
		public void SetUp()
		{
			_assemblies = new[] { typeof(DIContainer).Assembly };
			_usings = new[] { "DIApi.Methods" };

			_eval = new ApiEvaluator(_assemblies);
			_eval.SetUsings(_usings);
			_eval.SetAssemblyRefs(_assemblies);
		}

		[TearDown]
		public void TearDown()
		{
			_eval.EvaluateAndPrintToConsole();
		}
		#endregion

		[Test]
		public void Simple_Binding()
		{
			//var container = new DIContainer();
			//c.LoadBindingsFromCurrentAssembly();
			//Annotation: [BindingFor(typeof(IFoo))]
			//var instance = container.Get<IFoo>();

			_eval.NumberOfImplementationClasses(2);
			//_eval.ClassUsage<DIContainer>();
			_eval.Instantiation(() => new DIContainer());
			_eval.MethodCall((DIContainer c) => c.LoadBindingsFromCurrentAssembly());
			//_eval.ClassUsage<BindingForAttribute>();
			_eval.Instantiation(() => new BindingForAttribute(typeof(IFoo)));
			_eval.MethodCall((DIContainer c) => c.Get<IFoo>());
		}

		[Test]
		public void Named_Bindings()
		{
			//var container = new DIContainer();
			//c.LoadBindingsFromCurrentAssembly();
			//Annotation: [BindingFor(typeof(IFoo), Name = "binding1")]
			//Annotation: [BindingFor(typeof(IFoo), Name = "binding2")]
			//var instance = container.Get<IFoo>();

			_eval.NumberOfImplementationClasses(3);
			//_eval.ClassUsage<DIContainer>(1, 1);
			_eval.Instantiation(() => new DIContainer(),1,1);
			_eval.MethodCall((DIContainer c) => c.LoadBindingsFromCurrentAssembly(),1,1);
			//_eval.ClassUsage<BindingForAttribute>(2, 1);
			_eval.Instantiation(() => new BindingForAttribute(typeof(IFoo)),2,1);
			_eval.PropertyAccess((BindingForAttribute b) => b.Name,2);
			_eval.MethodCall((DIContainer c) => c.Get<IFoo>("name"), 2);
		}

		[Test]
		public void Constructor_Injection_simple()
		{
			//var container = new DIContainer();
			//Annotation: [BindingFor(typeof(IFoo))]
			//Annotation: [Inject]
			//var instance = container.Get<Service>();

			_eval.NumberOfImplementationClasses(3);
			//_eval.ClassUsage<DIContainer>(1, 2);
			_eval.Instantiation(() => new DIContainer(), 1, 2);
			_eval.MethodCall((DIContainer c) => c.LoadBindingsFromCurrentAssembly(), 1, 2);
			//_eval.ClassUsage<BindingForAttribute>(1, 3);
			_eval.Instantiation(() => new BindingForAttribute(typeof(IFoo)), 1, 3);
			//_eval.ClassUsage<InjectAttribute>();
			_eval.Instantiation(() => new InjectAttribute());
			_eval.MethodCall((DIContainer c) => c.Get<Service>(), 1, 1);
		}

		[Test]
		public void Constructor_Injection_complex()
		{
			//var container = new DIContainer();
			//Annotation: [BindingFor(typeof(IFoo), Name = "binding1")]
			//Annotation: [BindingFor(typeof(IFoo), Name = "binding2")]
			//Annotation: [Inject("binding1")]
			//Annotation: [Inject("binding2")]
			//var instance = container.Get<Service>();

			_eval.NumberOfImplementationClasses(4);
			//_eval.ClassUsage<DIContainer>(1, 3);
			_eval.Instantiation(() => new DIContainer(), 1, 3);
			_eval.MethodCall((DIContainer c) => c.LoadBindingsFromCurrentAssembly(), 1, 3);
			//_eval.ClassUsage<BindingForAttribute>(2, 4);
			_eval.Instantiation(() => new BindingForAttribute(typeof(IFoo)), 2, 4);
			_eval.PropertyAccess((BindingForAttribute b) => b.Name, 2, 2);
			//_eval.ClassUsage<InjectAttribute>(2, 1);
			_eval.Instantiation(() => new InjectAttribute("bindingName"), 2);
			_eval.MethodCall((DIContainer c) => c.Get<Service>(), 1, 2);
		}

		[Test]
		public void Binding_with_singleton_scope()
		{
			//var container = new DIContainer();
			//c.LoadBindingsFromCurrentAssembly();
			//Annotation: [BindingFor(typeof(IFoo))]
			//var instance = container.Get<IFoo>();

			_eval.NumberOfImplementationClasses(2);
			//_eval.ClassUsage<DIContainer>(1, 4);
			_eval.Instantiation(() => new DIContainer(), 1, 4);
			_eval.MethodCall((DIContainer c) => c.LoadBindingsFromCurrentAssembly(), 1, 4);
			//_eval.ClassUsage<BindingForAttribute>(1, 5);
			_eval.Instantiation(() => new BindingForAttribute(typeof(IFoo)), 1, 5);
			_eval.PropertyAccess((BindingForAttribute b) => b.Scope);
			_eval.MethodCall((DIContainer c) => c.Get<IFoo>(), 2, 3);
		}

		[Test]
		public void All()
		{
			//var container = new DIContainer();
			//c.LoadBindingsFromCurrentAssembly();
			//Annotation: [BindingFor(typeof(ILogger), Name="console", Scope=Scope.Singleton]
			//Annotation: [BindingFor(typeof(ILogger), Name="file")]
			//Annotation: [BindingFor(typeof(IAuthenticator), Name="authenticator")]
			//Annotation: [Inject]
			//Annotation: [Inject("file")]
			//Annotation: [Inject("console")]
			//Annotation: [Inject("console")]
			//Annotation: [Inject("authenticator")]
			//var instance = container.Get<IFoo>();

			_eval.NumberOfImplementationClasses(5);
			//_eval.ClassUsage<DIContainer>(1, 5);
			_eval.Instantiation(() => new DIContainer(), 1, 5);
			_eval.MethodCall((DIContainer c) => c.LoadBindingsFromCurrentAssembly(), 1, 5);

			//_eval.ClassUsage<InjectAttribute>(5, 3);
			_eval.Instantiation(() => new InjectAttribute(), 1, 1);
			_eval.Instantiation(() => new InjectAttribute("bindingname"), 4, 2);

			//_eval.ClassUsage<BindingForAttribute>(3, 5);
			_eval.Instantiation(() => new BindingForAttribute(typeof(IFoo)), 3, 5);
			_eval.PropertyAccess((BindingForAttribute b) => b.Name, 3, 2);
			_eval.PropertyAccess((BindingForAttribute b) => b.Scope, 1, 1);
			
			_eval.MethodCall((DIContainer c) => c.Get<Service>(), 1, 5);
		}
	}
}
